/*************************************************************************//**
 *****************************************************************************
 * @file   fs/open.c
 * The file contains:
 *   - do_open()
 *   - do_close()
 *   - do_lseek()
 *   - create_file()
 * @author Forrest Yu
 * @date   2007
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"

PRIVATE struct inode * create_file(char * path, int flags);
PRIVATE int alloc_imap_bit(int dev);
PRIVATE int alloc_smap_bit(int dev, int nr_sects_to_alloc);
PRIVATE struct inode * new_inode(int dev, int inode_nr, int start_sect);
PRIVATE void new_dir_entry(struct inode * dir_inode, int inode_nr, char * filename);

PRIVATE int find_entry(struct m_inode ** dir,const char * name, int namelen, struct dir_entry ** res_dir);

PRIVATE int bmap(struct m_inode * inode,int block);

PRIVATE int match(int len,const char * name,struct dir_entry * de);

PRIVATE struct m_inode * get_dir(char * pathname);

PRIVATE struct m_inode * dir_namei(const char * pathname,int * namelen, const char ** name);



PRIVATE struct m_inode * dir_namei(const char * pathname,
	int * namelen, const char ** name)
{
	char c;
	const char * basename;
	struct m_inode * dir;

	if (!(dir = get_dir(pathname)))
		return NULL;
	basename = pathname;
	while (c=*pathname++)
		if (c=='/')
			basename=pathname;
	*namelen = pathname-basename-1;
	*name = basename;
	return dir;
}

PRIVATE struct m_inode * get_dir(char * pathname){
	char c;
	const char * thisname;
	struct m_inode * inode;
	
	int namelen,inr,idev;
	struct dir_entry * de;
	if ((c=*pathname)=='/') {
		inode = root;
		pathname++;
	} else 
		inode = pwd;
	
	while (1) {
		thisname = pathname;
		if (!S_ISDIR(inode->i_mode) ) {
			iput(inode);
			return NULL;
		}
		//printl("pathname:%s\n",pathname);
		for(namelen=0;((c=*pathname++)&&(c!='/'));namelen++)
			//printl("c: %c\n",c)
			;
		//printl("len:%d, pathname:%s\n",namelen,pathname);
		if (!c)
			return inode;
		if (!find_entry(&inode,thisname,namelen,&de)) {
			iput(inode);
			return NULL;
		}
		inr = de->inode_nr;
		idev = inode->i_dev;
		
		iput(inode);
		inode = iget(idev,inr);
	}
}
PRIVATE int find_entry(struct m_inode ** dir,const char * name, int namelen, struct dir_entry ** res_dir)
{
	int entries;
	int block,i=0;
	struct dir_entry * de;
	entries = (*dir)->i_size / (sizeof (struct dir_entry));
	printl("entries:%d\n",entries);
	if (!namelen)
		return 0;
	if (!(block = (*dir)->i_zone[0]))
		return 0;
	//printl("dev:%d,block:%d\n",(*dir)->i_dev,block);
	new_read((*dir)->i_dev,block);

	printl("entry name need : %s\n",name);
	de = (struct dir_entry *)fsbuf;
	//printl("de->inode_nr: %d,de->name: %s",de->inode_nr,de->name);
	/*above is right*/
	while (i < entries) {
		if ((char *)de >= BLOCK_SIZE+fsbuf) {

			if (!(block = bmap(*dir,i/DIR_ENTRIES_PER_BLOCK))) {

				i += DIR_ENTRIES_PER_BLOCK;
				continue;
			}
			new_read((*dir)->i_dev,block); /* new read 错误处理没有做*/
			de = (struct dir_entry *)fsbuf;
		}
		if (match(namelen,name,de)) {
			*res_dir = de;
			return 1;
		}
		de++;
		i++;
	}
	return 0;
}
PUBLIC int handle_list()
{
	const char * basename;
	int inr,idev,namelen;
	
	int fd = fs_msg.FD;	/**< file descriptor. */
	void * buf = fs_msg.BUF;/**< r/w buffer */
	int flags = fs_msg.FLAGS;
	char flag[1];	
	int name_len = fs_msg.NAME_LEN;	
	int j,prev=0;
	struct m_inode ** dir;
	int src = fs_msg.source;
	int entries;
	int block,i=0;
	struct dir_entry * de;
	
	char pathname[MAX_PATH];
	phys_copy((void*)va2la(TASK_FS, pathname),
		  (void*)va2la(src, fs_msg.PATHNAME),
		  name_len);
	pathname[name_len] = 0;

	struct m_inode * inode = dir_namei(pathname,&namelen,&basename);
	struct m_inode * inode2;
	printl("inode_nr is %d,basename is %s", inode->i_num,basename);
	
	if(find_entry(&inode,basename,namelen,&de))
		 *dir = iget(inode->i_dev,de->inode_nr);
	
	pwd = *dir;
	
	entries = (*dir)->i_size / (sizeof (struct dir_entry));
	printl("entries:%d\n",entries);
	if (!namelen)
		return 0;
	if (!(block = (*dir)->i_zone[0]))
		return 0;
	printl("dev2:%d,block:%d\n",(*dir)->i_dev,block);
	new_read((*dir)->i_dev,block);
	//printl("tmp4");
	
	de = (struct dir_entry *)fsbuf;
	//printl("de->inode_nr: %d,de->name: %s",de->inode_nr,de->name);
	/*above is right*/
	i=i+2;
	de +=2;
	//entries -= 4;
	while (i < entries) {
		 
		//printl("tmp1");
		if (!(block = bmap(*dir,i/DIR_ENTRIES_PER_BLOCK))) {
			i += DIR_ENTRIES_PER_BLOCK;
			continue;
		}
		//printl("tmp2");
		new_read((*dir)->i_dev,block); /* new read 错误处理没有做*/
		//if ((char *)de >= BLOCK_SIZE+fsbuf)
		//de = (struct dir_entry *)fsbuf;
		//printl("tmp3");
		
		inr = de->inode_nr;

		idev = inode->i_dev;
		//printl("inode number: %d",de->inode_nr);
		j=0;
		while(*(de->name+j)) 
			j++;
		
		//printl("%s\n",de->name);

		//new_read((*dir)->i_dev,block);

		phys_copy((void*)va2la(src, buf+prev+1),(void*)va2la(TASK_FS, de->name),j);
		inode2 = iget(0x301,de->inode_nr);
		if(S_ISDIR(inode2->i_mode))
			flag[0]='D';
		else
			flag[0]='F';
		phys_copy((void*)va2la(src, buf+prev),(void*)va2la(TASK_FS, flag),1);
		iput(inode2);
		
		prev += j+1;
		
		de++;
		i++;
	}
	printl("finish\n");
	return entries;
}

PUBLIC int new_file_read()
{
	
	int fd = fs_msg.FD;	/**< file descriptor. */
	void * buf = fs_msg.BUF;/**< r/w buffer */
	int len = fs_msg.CNT;	/**< r/w bytes */

	int src = fs_msg.source;		/* caller proc nr. */
	struct file * read_file = fileTable + fd;
	struct m_inode * inode = read_file->f_inode;
	int offset=0,block;
	len = (len < inode->i_size)? len : inode->i_size;
	
	printl("inode_size: %d",inode->i_size);
	while(offset<len)
	{
		if(!(block = bmap(inode, offset/BLOCK_SIZE))){
			offset += BLOCK_SIZE;
			continue;
		}
		new_read(inode->i_dev,block);
		if(offset+BLOCK_SIZE<=len)
			phys_copy((void*)va2la(src, buf+offset),(void*)va2la(TASK_FS, fsbuf),BLOCK_SIZE);
		else
			phys_copy((void*)va2la(src, buf+offset),(void*)va2la(TASK_FS, fsbuf),len - offset);
		offset += BLOCK_SIZE;
	}
	//printl("read things: %s",buf);
	return len;
			
}

PRIVATE int bmap(struct m_inode * inode,int block)
{
	struct buffer_head * bh;
	int i;

	if (block<0)
		panic("_bmap: block<0");
	if (block >= 7+512+512*512)
		panic("_bmap: block>big");
	if (block<7) {
//		if (create && !inode->i_zone[block])
//			if ((inode->i_zone[block]=new_block(inode->i_dev))) {
//				inode->i_ctime=CURRENT_TIME;
//				inode->i_dirt=1;
//			}
		return inode->i_zone[block];
	}
	block -= 7;
	if (block<512) {
//		if (create && !inode->i_zone[7])
//			if ((inode->i_zone[7]=new_block(inode->i_dev))) {
//				inode->i_dirt=1;
//				inode->i_ctime=CURRENT_TIME;
//			}
		if (!inode->i_zone[7])
			return 0;
//		if (!(bh = bread(inode->i_dev,inode->i_zone[7])))
//			return 0;
		new_read(inode->i_dev,inode->i_zone[7]);
		i = ((unsigned short *) (fsbuf))[block];
//		if (create && !i)
//			if ((i=new_block(inode->i_dev))) {
//				((unsigned short *) (bh->b_data))[block]=i;
//				bh->b_dirt=1;
//			}
		
		return i;
	}
	return 0;

}
PRIVATE int match(int len,const char * name,struct dir_entry * de)
{
	


	if (!de || !de->inode_nr)
		return 0;
	if (de->name[len])
		return 0;
	int i=0;
	while (i<len)
	{
		if(name[i]!=de->name[i++])
			return 0;
		
	}

	return 1;
}

PUBLIC int new_open()
{

	const char * basename;
	int inr,dev,namelen,fp,i;
	struct m_inode * dir;
	
	struct dir_entry * de;
	
	char pathname[MAX_PATH];
	int flags = fs_msg.FLAGS;	
	int name_len = fs_msg.NAME_LEN;	
	int src = fs_msg.source;	
	assert(name_len < MAX_PATH);
	phys_copy((void*)va2la(TASK_FS, pathname),
		  (void*)va2la(src, fs_msg.PATHNAME),
		  name_len);
	pathname[name_len] = 0;

	struct file * open_file = fileTable;
/* we think file table is always available */
	for(i=0;i<32;i++)
	{
		if(!(open_file->f_count))
			break;
		open_file++;
	}
	if(i==32)
		return -1;
	else
		fp = i; /* 文件句柄*/
	open_file->f_count++;
//	char pathname[100]="/usr/root/hello.c";
	struct m_inode * inode = dir_namei(pathname,&namelen,&basename);
	if(i!=NULL)
		printl("inode_nr is %d,basename is %s", inode->i_num,basename);
	
	if(find_entry(&inode,basename,namelen,&de))
		open_file->f_inode = iget(inode->i_dev,de->inode_nr);
	else 	
		return -1;
	iput(inode);
	if(open_file->f_inode){
		printl("open inode: %d\n",fileTable[fp].f_inode->i_num);
		return fp;

	}
	//if(find_entry(&root,pathname,8,&de))
	//	printl("dev inode is %d", de->inode_nr);
//	else
//		printl("find entry error");

	
}



PUBLIC int do_close()
{
	int fd = fs_msg.FD;
	fileTable[fd].f_count--;
	iput(fileTable[fd].f_inode);

	return 0;
}


